#!/usr/bin/env python
# encoding: utf-8
'''
@author: Excelsiorly
@license: (C) Copyright 2022, All Rights Reserved.
@contact: excelsiorly@qq.com
@file: 031. 下一个排列.py
@time: 2022/1/16 15:35
@desc: https://leetcode-cn.com/problems/next-permutation/
> 整数数组的一个 排列  就是将其所有成员以序列或线性顺序排列。

例如，arr = [1,2,3] ，以下这些都可以视作 arr 的排列：[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地，如果数组的所有排列根据其字典顺序从小到大排列在一个容器中，那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列，那么这个数组必须重排为字典序最小的排列（即，其元素按升序排列）。

例如，arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地，arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ，因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ，找出 nums 的下一个排列。

必须 原地 修改，只允许使用额外常数空间。

1. Ot(N), Os(1)

@解题思路：
    方法一：两遍扫描
        思路及解法

        注意到下一个排列总是比当前排列要大，除非该排列已经是最大的排列。我们希望找到一种方法，能够找到一个大于当前序列的新序列，且变大的幅度尽可能小。具体地：

        我们需要将一个左边的「较小数」与一个右边的「较大数」交换，以能够让当前排列变大，从而得到下一个排列。

        同时我们要让这个「较小数」尽量靠右，而「较大数」尽可能小。当交换完成后，「较大数」右边的数需要按照升序重新排列。
        这样可以在保证新排列大于原来排列的情况下，使变大的幅度尽可能小。

        以排列 [4,5,2,6,3,1][4,5,2,6,3,1] 为例：

        我们能找到的符合条件的一对「较小数」与「较大数」的组合为 22 与 33，满足「较小数」尽量靠右，而「较大数」尽可能小。

        当我们完成交换后排列变为 [4,5,3,6,2,1][4,5,3,6,2,1]，此时我们可以重排「较小数」右边的序列，序列变为 [4,5,3,1,2,6][4,5,3,1,2,6]。

        具体地，我们这样描述该算法，对于长度为 nn 的排列 aa：

        首先从后向前查找第一个顺序对 (i,i+1)(i,i+1)，满足 a[i] < a[i+1]a[i]<a[i+1]。这样「较小数」即为 a[i]a[i]。此时 [i+1,n)[i+1,n) 必然是下降序列。

        如果找到了顺序对，那么在区间 [i+1,n)[i+1,n) 中从后向前查找第一个元素 jj 满足 a[i] < a[j]a[i]<a[j]。这样「较大数」即为 a[j]a[j]。

        交换 a[i]a[i] 与 a[j]a[j]，此时可以证明区间 [i+1,n)[i+1,n) 必为降序。我们可以直接使用双指针反转区间 [i+1,n)[i+1,n) 使其变为升序，
        而无需对该区间进行排序。

'''
class Solution(object):
    def nextPermutation(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        例子：452631
        """
        n = len(nums)
        i = n - 2
        # 从后往前找第一小于其后者的元素
        while i >= 0 and nums[i] >= nums[i + 1]:
            i -= 1
        # 找的到话，i一定大于等于0
        if i >= 0:
            # 此时再从[i+1, n)的区间内从后往前找到第一个大于元素i的元素j
            j = len(nums) - 1
            # 一定能找到，因为i至少一定比他后面一个元素小
            while j >= 0 and nums[i] >= nums[j]:
                j -= 1
            # 交换位置
            nums[i], nums[j] = nums[j], nums[i]

        # 此时，无论是nums完全是逆序的（此时i=-1），还是说找的到，则(i+1, n)都一定是逆序的，则将整个区间反转
        left, right = i + 1, n - 1
        while left < right:
            nums[left], nums[right] = nums[right], nums[left]
            left += 1
            right -= 1


if __name__ == '__main__':
    nums = [3,2,1]
    res = Solution().nextPermutation(nums)
    print(res)